package cn.freeprogrammer.igen.data;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import cn.freeprogrammer.igen.data.type.FieldTypeUtils;
import cn.freeprogrammer.igen.utils.DefaultNameResolver;
import cn.freeprogrammer.igen.utils.NameResolver;

public class ProjectData {

	private List<EntityData> entityDatas = new ArrayList<EntityData>();

	private NameResolver nameResolver;

	private Connection conn;

	private String basePackage;

	private String sqlMapPath;
	
	private String sqlMapConfigPath;

	private String entityBasePath;

	private String daoBasePath;

	private String entityPackage;

	private String daoPackage;

	private boolean generateSqlMapConfig = true;
	
	private boolean generateSqlMap = true;

	private boolean generateEntity = true;

	private boolean generateDao = true;

	public static ProjectData newInstance(String configFile) throws Exception {
		SAXReader reader = new SAXReader();

		Document document = reader.read(configFile);

		Element root = document.getRootElement();

		// JDBC
		Element jdbcElement = root.element("jdbc");
		String jdbcDriverClassName = jdbcElement.attributeValue("driverClassName");
		String jdbcUrl = jdbcElement.attributeValue("url");
		String jdbcUser = jdbcElement.attributeValue("user");
		String jdbcPassword = jdbcElement.attributeValue("password");

		// basepackage
		String basePackage = root.element("project").attributeValue("basePackage");

		// project data
		ProjectData projectData = new ProjectData(jdbcDriverClassName, jdbcUrl, jdbcUser,
				jdbcPassword, new DefaultNameResolver(), basePackage);

		// sqlmap config generator
		Element scElement = root.element("sqlMapConfigGenerator");
		if (scElement != null) {
			String targetPath = scElement.attributeValue("targetPath");
			projectData.setSqlMapConfigPath(targetPath);
		} else {
			projectData.setGenerateSqlMapConfig(false);
		}

		
		// sqlmap generator
		Element sgElement = root.element("sqlMapGenerator");
		if (sgElement != null) {
			String targetPath = sgElement.attributeValue("targetPath");
			projectData.setSqlMapPath(targetPath);
		} else {
			projectData.setGenerateSqlMap(false);
		}

		// entity generator
		Element egElement = root.element("entityGenerator");
		if (egElement != null) {
			String targetPackage = egElement.attributeValue("targetPackage");
			String targetPath = egElement.attributeValue("targetPath");
			projectData.setEntityPackage(targetPackage);
			projectData.setEntityBasePath(targetPath);
		} else {
			projectData.setGenerateEntity(false);
		}

		// dao generator
		Element dgElement = root.element("daoGenerator");
		if (dgElement != null) {
			String targetPackage = dgElement.attributeValue("targetPackage");
			String targetPath = dgElement.attributeValue("targetPath");
			projectData.setDaoPackage(targetPackage);
			projectData.setDaoBasePath(targetPath);
		} else {
			projectData.setGenerateDao(false);
		}

		// add entity datas to project data
		Iterator iter = root.elementIterator("table");
		while (iter.hasNext()) {
			Element tableElement = (Element) iter.next();
			String tableName = tableElement.attributeValue("tableName");
			String entityName = tableElement.attributeValue("entityName");
			projectData.addEntityData(tableName, entityName);
		}

		return projectData;
	}

	private ProjectData(String driverClassName, String url, String user, String password,
			NameResolver nameResolver, String basePackage)
			throws Exception {
		this.nameResolver = nameResolver;
		this.basePackage = basePackage;

		Class.forName(driverClassName);
		conn = DriverManager.getConnection(url, user, password);
	}

	public void addEntityData(String tableName) throws Exception {
		addEntityData(tableName, nameResolver.entityName(tableName));
	}

	public void addEntityData(String tableName, String entityName) throws Exception {
		EntityData entityData = new EntityData();

		entityData.setPackageName(entityPackage);
		entityData.setTableName(tableName);
		entityData.setEntityName(entityName);
		entityData.setFullEntityName(entityPackage + "." + entityName);

		setEntityPrimaryKey(entityData);

		setEntityFields(entityData);

		entityDatas.add(entityData);
	}

	private void setEntityFields(EntityData entityData) throws Exception {
		String tableName = entityData.getTableName();
		ResultSet rs = conn.getMetaData().getColumns(conn.getCatalog(), null, tableName, null);
		while (rs.next()) {
			FieldData c = new FieldData();
			String columnName = rs.getString(4);
			c.setColumnName(columnName);
			c.setFieldName(nameResolver.fieldName(columnName));
			c.setFieldType(FieldTypeUtils.getFieldType(rs.getInt(5)));
			entityData.addField(c);
		}

		return;
	}

	private void setEntityPrimaryKey(EntityData entityData) throws Exception {
		String tableName = entityData.getTableName();
		ResultSet rs = conn.getMetaData().getPrimaryKeys(conn.getCatalog(), null, tableName);
		rs.next();
		if (rs.getRow() > 0) {
			FieldData c = new FieldData();
			String columnName = rs.getString(4);
			c.setColumnName(columnName);
			c.setFieldName(nameResolver.fieldName(columnName));
			ResultSet rs2 = conn.getMetaData().getColumns(conn.getCatalog(), null, tableName,
					columnName);
			rs2.next();
			if (rs2.getRow() > 0) {
				c.setFieldType(FieldTypeUtils.getFieldType(rs2.getInt(5)));
			}
			entityData.setPrimaryKey(c);
		}

		return;
	}

	public List<EntityData> getEntityDatas() {
		return entityDatas;
	}

	public String getBasePackage() {
		return basePackage;
	}

	public void setBasePackage(String basePackage) {
		this.basePackage = basePackage;
	}

	public String getEntityPackage() {
		return entityPackage;
	}

	public void setEntityPackage(String entityPackage) {
		this.entityPackage = entityPackage;
	}

	public String getDaoPackage() {
		return daoPackage;
	}

	public void setDaoPackage(String daoPackage) {
		this.daoPackage = daoPackage;
	}

	public String getSqlMapPath() {
		return sqlMapPath;
	}

	public void setSqlMapPath(String sqlMapPath) {
		this.sqlMapPath = sqlMapPath;
	}

	public String getEntityBasePath() {
		return entityBasePath;
	}

	public void setEntityBasePath(String entityBasePath) {
		this.entityBasePath = entityBasePath;
	}

	public String getDaoBasePath() {
		return daoBasePath;
	}

	public void setDaoBasePath(String daoBasePath) {
		this.daoBasePath = daoBasePath;
	}

	public boolean isGenerateSqlMap() {
		return generateSqlMap;
	}

	public void setGenerateSqlMap(boolean generateSqlMap) {
		this.generateSqlMap = generateSqlMap;
	}

	public boolean isGenerateDao() {
		return generateDao;
	}

	public void setGenerateDao(boolean generateDao) {
		this.generateDao = generateDao;
	}

	public boolean isGenerateEntity() {
		return generateEntity;
	}

	public void setGenerateEntity(boolean generateEntity) {
		this.generateEntity = generateEntity;
	}

	public String getSqlMapConfigPath() {
		return sqlMapConfigPath;
	}

	public void setSqlMapConfigPath(String sqlMapConfigPath) {
		this.sqlMapConfigPath = sqlMapConfigPath;
	}

	public boolean isGenerateSqlMapConfig() {
		return generateSqlMapConfig;
	}

	public void setGenerateSqlMapConfig(boolean generateSqlMapConfig) {
		this.generateSqlMapConfig = generateSqlMapConfig;
	}
}
